home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / pine / mailpart.c < prev    next >
C/C++ Source or Header  |  1996-05-29  |  66KB  |  2,571 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: mailpart.c,v 4.124 1996/05/29 21:47:03 mikes Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.             T H E    P I N E    M A I L   S Y S T E M
  7.  
  8.    Laurence Lundblade and Mike Seibel
  9.    Networks and Distributed Computing
  10.    Computing and Communications
  11.    University of Washington
  12.    Administration Builiding, AG-44
  13.    Seattle, Washington, 98195, USA
  14.    Internet: lgl@CAC.Washington.EDU
  15.              mikes@CAC.Washington.EDU
  16.  
  17.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  18.  
  19.  
  20.    Pine and Pico are registered trademarks of the University of Washington.
  21.    No commercial use of these trademarks may be made without prior written
  22.    permission of the University of Washington.
  23.  
  24.    Pine, Pico, and Pilot software and its included text are Copyright
  25.    1989-1996 by the University of Washington.
  26.  
  27.    The full text of our legal notices is contained in the file called
  28.    CPYRIGHT, included with this distribution.
  29.  
  30.  
  31.    Pine is in part based on The Elm Mail System:
  32.     ***********************************************************************
  33.     *  The Elm Mail System  -  Revision: 2.13                             *
  34.     *                                                                     *
  35.     *             Copyright (c) 1986, 1987 Dave Taylor              *
  36.     *             Copyright (c) 1988, 1989 USENET Community Trust   *
  37.     ***********************************************************************
  38.  
  39.  
  40.   ----------------------------------------------------------------------*/
  41.  
  42. /*======================================================================
  43.      mailpart.c
  44.      The meat and pototoes of attachment processing here.
  45.  
  46.   ====*/
  47.  
  48. #include "headers.h"
  49.  
  50.  
  51. /*
  52.  * Information used to paint and maintain a line on the attachment
  53.  * screen.
  54.  */
  55. typedef struct atdisp_line {
  56.     char         *dstring;            /* alloc'd var value string  */
  57.     ATTACH_S         *attp;            /* actual attachment pointer */
  58.     struct atdisp_line *next, *prev;
  59. } ATDISP_S;
  60.  
  61.  
  62. /*
  63.  * struct defining attachment screen's current state
  64.  */
  65. typedef struct att_screen {
  66.     ATDISP_S  *current,
  67.           *top_line;
  68. } ATT_SCREEN_S;
  69. static ATT_SCREEN_S *att_screen;
  70.  
  71.  
  72. #define    next_attline(p)    ((p) ? (p)->next : NULL)
  73. #define    prev_attline(p)    ((p) ? (p)->prev : NULL)
  74.  
  75.  
  76. static struct key att_index_keys[] = 
  77.        {{"?","Help",KS_SCREENHELP},    {NULL,NULL,KS_NONE},
  78.     {"E","Exit Index",KS_EXITMODE},    {"V","[View]",KS_VIEW},
  79.     {"P","PrevAttch",KS_NONE},    {"N","NextAttch",KS_NONE},
  80.     {"-","PrevPage",KS_PREVPAGE},    {"Spc","NextPage",KS_NEXTPAGE},
  81.     {"A","AboutAttch",KS_NONE},    {"S","Save",KS_SAVE},
  82.     {"|","Pipe",KS_NONE},        {"W","WhereIs",KS_WHEREIS}};
  83. INST_KEY_MENU(att_index_keymenu, att_index_keys);
  84. #define    ATT_PIPE_KEY    10
  85.  
  86. /* used to keep track of detached MIME segments total length */
  87. static long    save_att_length;
  88.  
  89. /*
  90.  * Internal Prototypes
  91.  */
  92. int      display_attachment PROTO((long, ATTACH_S *));
  93. void      display_text_att PROTO((long, ATTACH_S *));
  94. void      display_msg_att PROTO((long, ATTACH_S *));
  95. void      display_abook_att PROTO((long, ATTACH_S *));
  96. void      display_attach_info PROTO((long, ATTACH_S *));
  97. void      run_viewer PROTO((char *, BODY *));
  98. ATDISP_S *new_attline PROTO((ATDISP_S **));
  99. void      free_attline PROTO((ATDISP_S **));
  100. ATDISP_S *first_attline PROTO((ATDISP_S *));
  101. void      attachment_screen_redrawer PROTO(());
  102. int      attachment_screen_updater PROTO((struct pine *, ATDISP_S *, \
  103.                        ATT_SCREEN_S *));
  104. int      init_att_progress PROTO((char *, MAILSTREAM *, BODY *));
  105. long      save_att_piped PROTO((int));
  106. int      save_att_percent PROTO(());
  107. int      df_trigger_cmp PROTO((long, char *));
  108. int      df_trigger_cmp_text PROTO((char *, char *));
  109. int      df_trigger_cmp_lwsp PROTO((char *, char *));
  110. int      df_trigger_cmp_start PROTO((char *, char *));
  111. int      df_static_trigger PROTO((BODY *));
  112. int      df_valid_command PROTO((char **));
  113. char     *dfilter PROTO((char *, STORE_S *, gf_io_t, filter_t *));
  114.  
  115.  
  116. #if defined(DOS) || defined(OS2)
  117. #define    READ_MODE    "rb"
  118. #define    WRITE_MODE    "wb"
  119. #else
  120. #define    READ_MODE    "r"
  121. #define    WRITE_MODE    "w"
  122. #endif
  123.  
  124.  
  125.  
  126. /*----------------------------------------------------------------------
  127.    Provide attachments in browser for selected action
  128.  
  129.   Args: ps -- pointer to pine structure
  130.     msgmap -- struct containing current message to display
  131.  
  132.   Result: 
  133.  
  134.   Handle painting and navigation of attachment index.  It would be nice
  135.   to someday handle message/rfc822 segments in a neat way (i.e., enable
  136.   forwarding, take address, etc.).
  137.  ----*/
  138. void
  139. attachment_screen(ps, msgmap)
  140.     struct pine *ps;
  141.     MSGNO_S     *msgmap;
  142. {
  143.     int          i, ch = 'x', orig_ch, done = 0, changes = 0, dline,
  144.           nl = 0, sl = 0, old_cols = -1, km_popped = 0;
  145.     long      msgno;
  146.     char     *p, *q;
  147.     ATTACH_S     *atmp;
  148.     ATDISP_S     *current = NULL, *ctmp = NULL;
  149.     ATT_SCREEN_S  screen;
  150.  
  151.     if(mn_total_cur(msgmap) > 1L){
  152.     q_status_message(SM_ORDER | SM_DING, 0, 3,
  153.              "Can only view one message's attachments at a time!");
  154.     return;
  155.     }
  156.     else if(ps->atmts && !(ps->atmts + 1)->description)
  157.       q_status_message1(SM_ASYNC, 0, 3,
  158.     "Message %s has only one part (the message body), and no attachments.",
  159.     long2string(mn_get_cur(msgmap)));
  160.  
  161.     /*
  162.      * find the longest number and size strings
  163.      */
  164.     for(atmp = ps->atmts; atmp && atmp->description; atmp++){
  165.     if((i = strlen(atmp->number)) > nl)
  166.       nl = i;
  167.  
  168.     if((i = strlen(atmp->size)) > sl)
  169.       sl = i;
  170.     }
  171.  
  172.     /*
  173.      * Then, allocate and initialize attachment line list...
  174.      */
  175.     for(atmp = ps->atmts; atmp && atmp->description; atmp++)
  176.       new_attline(¤t)->attp = atmp;
  177.  
  178.     screen.current     = screen.top_line = NULL;
  179.     msgno           = mn_m2raw(msgmap, mn_get_cur(msgmap));
  180.     ps->mangled_screen = 1;            /* build display */
  181.     ps->redrawer       = attachment_screen_redrawer;
  182.     att_screen           = &screen;
  183.     current           = first_attline(current);
  184.     if(ctmp = next_attline(current))
  185.       current = ctmp;
  186.  
  187.     while(!done){
  188.     if(km_popped){
  189.         km_popped--;
  190.         if(km_popped == 0){
  191.         clearfooter(ps);
  192.         ps->mangled_body = 1;
  193.         }
  194.     }
  195.  
  196.     if(ps->mangled_screen){
  197.         /*
  198.          * build/rebuild display lines
  199.          */
  200.         if(old_cols != ps->ttyo->screen_cols){
  201.         old_cols = ps->ttyo->screen_cols;
  202.         for(ctmp = first_attline(current);
  203.             ctmp && (atmp = ctmp->attp) && atmp->description;
  204.             ctmp = next_attline(ctmp)){
  205.             size_t len, dlen;
  206.  
  207.             if(ctmp->dstring)
  208.               fs_give((void **)&ctmp->dstring);
  209.  
  210.             len = max(80, ps->ttyo->screen_cols) * sizeof(char);
  211.             ctmp->dstring = (char *)fs_get(len + 1);
  212.             ctmp->dstring[len] = '\0';
  213.             memset(ctmp->dstring, ' ', len);
  214.             p = ctmp->dstring + 3;
  215.             for(i = 0; atmp->number[i]; i++)
  216.               *p++ = atmp->number[i];
  217.  
  218.             /* add 3 spaces, plus pad number, and right justify size */
  219.             p += 3 + nl - i + (sl - strlen(atmp->size));
  220.  
  221.             for(i = 0; atmp->size[i]; i++)
  222.               *p++ = atmp->size[i];
  223.  
  224.             p += 3;
  225.  
  226.             /* copy the type description */
  227.             q = type_desc(atmp->body->type, atmp->body->subtype,
  228.                   atmp->body->parameter, 1);
  229.             if((dlen = strlen(q)) > (i = len - (p - ctmp->dstring)))
  230.               dlen = i;
  231.  
  232.             strncpy(p, q, dlen);
  233.             p += dlen;
  234.  
  235.             /* provided there's room, copy the user's description */
  236.             if(len - (p - ctmp->dstring) > 8
  237.                && ((q = atmp->body->description)
  238.                || (atmp->body->type == TYPEMESSAGE
  239.                    && atmp->body->subtype
  240.                    && strucmp(atmp->body->subtype, "rfc822") == 0
  241.                    && atmp->body->contents.msg.env
  242.                    && (q=atmp->body->contents.msg.env->subject)))){
  243.             sstrcpy(&p, ", \"");
  244.             if((dlen=strlen(q)) > (i=len - (p - ctmp->dstring)))
  245.               dlen = i;
  246.  
  247.             strncpy(p, q, dlen);
  248.             *(p += dlen) = '\"';
  249.             }
  250.         }
  251.         }
  252.  
  253.         ps->mangled_header = 1;
  254.         ps->mangled_footer = 1;
  255.         ps->mangled_body   = 1;
  256.     }
  257.  
  258.     /*----------- Check for new mail -----------*/
  259.         if(new_mail(0, NM_TIMING(ch), 1) >= 0)
  260.           ps->mangled_header = 1;
  261.  
  262.     if(ps->mangled_header){
  263.         set_titlebar("ATTACHMENT INDEX", ps->mail_stream,
  264.              ps->context_current, ps->cur_folder, ps->msgmap, 1,
  265.              MessageNumber, 0, 0);
  266.         ps->mangled_header = 0;
  267.     }
  268.  
  269.     if(ps->mangled_screen){
  270.         ClearLine(1);
  271.         ps->mangled_screen = 0;
  272.     }
  273.  
  274.     dline = attachment_screen_updater(ps, current, &screen);
  275.  
  276.     /*---- This displays new mail notification, or errors ---*/
  277.     if(km_popped){
  278.         FOOTER_ROWS(ps) = 3;
  279.         mark_status_unknown();
  280.     }
  281.  
  282.         display_message(ch);
  283.     if(km_popped){
  284.         FOOTER_ROWS(ps) = 1;
  285.         mark_status_unknown();
  286.     }
  287.  
  288.         /*------ Read the command from the keyboard ----*/
  289.     if(ps->mangled_footer){
  290.         bitmap_t     bitmap;
  291.  
  292.         setbitmap(bitmap);
  293.         ps->mangled_footer = 0;
  294.         if(F_OFF(F_ENABLE_PIPE, ps))
  295.           clrbitn(ATT_PIPE_KEY, bitmap);    /* always clear for DOS */
  296.  
  297.         if(km_popped){
  298.         FOOTER_ROWS(ps) = 3;
  299.         clearfooter(ps);
  300.         }
  301.  
  302.         draw_keymenu(&att_index_keymenu, bitmap, ps->ttyo->screen_cols,
  303.              1-FOOTER_ROWS(ps), 0, FirstMenu,0);
  304.         if(km_popped){
  305.         FOOTER_ROWS(ps) = 1;
  306.         mark_keymenu_dirty();
  307.         }
  308.     }
  309.  
  310.     if(F_ON(F_SHOW_CURSOR, ps))
  311.       MoveCursor(dline, 0);
  312.     else
  313.       MoveCursor(max(0, ps->ttyo->screen_rows - FOOTER_ROWS(ps)), 0);
  314.  
  315. #ifdef    MOUSE
  316.     mouse_in_content(KEY_MOUSE, -1, -1, 0, 0); /* prime the handler */
  317.     register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
  318.                ps_global->ttyo->screen_rows -(FOOTER_ROWS(ps_global)+1),
  319.                ps_global->ttyo->screen_cols);
  320. #endif
  321.     ch = orig_ch = read_command();
  322. #ifdef    MOUSE
  323.     clear_mfunc(mouse_in_content);
  324. #endif
  325.  
  326.         if(ch <= 0xff && isupper((unsigned char)ch))
  327.           ch = tolower((unsigned char)ch);
  328.  
  329.     if(km_popped)
  330.       switch(ch){
  331.         case NO_OP_IDLE:
  332.         case NO_OP_COMMAND: 
  333.         case KEY_RESIZE:
  334.         case ctrl('L'):
  335.           km_popped++;
  336.           break;
  337.         
  338.         default:
  339.           clearfooter(ps);
  340.           break;
  341.       }
  342.  
  343.     switch(ch){
  344.       case '?' :                /* help! */
  345.       case ctrl('G'):
  346.       case PF1 :
  347.         if(FOOTER_ROWS(ps) == 1 && km_popped == 0){
  348.         km_popped = 2;
  349.         ps->mangled_footer = 1;
  350.         break;
  351.         }
  352.  
  353.         helper(h_attachment_screen, "HELP FOR ATTACHMENT INDEX", 0);
  354.         ps->mangled_screen = 1;
  355.         break;
  356.  
  357.       case 'e' :                /* exit attachment screen */
  358.       case PF3 :
  359.         done++;
  360.         break;
  361.  
  362.       case 'n' :                /* next list element */
  363.       case '\t' :
  364.       case ctrl('F') :
  365.       case KEY_RIGHT :
  366.       case ctrl('N'):            /* down arrow */
  367.       case KEY_DOWN :
  368.       case PF6 :
  369.         ch = KEY_DOWN;
  370.         if(ctmp = next_attline(current))
  371.           current = ctmp;
  372.         else
  373.           q_status_message(SM_ORDER, 0, 1,
  374.                    "Already on last attachment");
  375.  
  376.         break;
  377.  
  378.       case 'p' :                /* previous list element */
  379.       case ctrl('B') :
  380.       case KEY_LEFT :
  381.       case ctrl('P') :            /* up arrow */
  382.       case KEY_UP :
  383.       case PF5 :
  384.         ch = KEY_UP;
  385.         if(ctmp = prev_attline(current))
  386.           current = ctmp;
  387.         else
  388.           q_status_message(SM_ORDER, 0, 1,
  389.                    "Already on first attachment");
  390.  
  391.         break;
  392.  
  393.       case '+' :                /* page forward */
  394.       case ' ' :
  395.       case ctrl('V') :
  396.       case PF8 :
  397.         ch = KEY_DOWN;
  398.         if(next_attline(current)){
  399.         while(dline++ < ps->ttyo->screen_rows - FOOTER_ROWS(ps))
  400.           if(ctmp = next_attline(current))
  401.             current = ctmp;
  402.           else
  403.             break;
  404.         }
  405.         else
  406.           q_status_message(SM_ORDER, 0, 1,
  407.                    "Already on last page of attachments");
  408.         
  409.  
  410.         break;
  411.  
  412.       case '-' :                /* page backward */
  413.       case ctrl('Y') :
  414.       case PF7 :
  415.         ch = KEY_UP;
  416.         if(prev_attline(current)){
  417.         while(dline-- > HEADER_ROWS(ps))
  418.           if(ctmp = prev_attline(current))
  419.             current = ctmp;
  420.           else
  421.             break;
  422.  
  423.         while(++dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps))
  424.           if(ctmp = prev_attline(current))
  425.             current = ctmp;
  426.           else
  427.             break;
  428.         }
  429.         else
  430.           q_status_message(SM_ORDER, 0, 1,
  431.                    "Already on first page of attachments");
  432.  
  433.         break;
  434.  
  435. #ifdef MOUSE        
  436.       case KEY_MOUSE:
  437.         {
  438.         MOUSEPRESS mp;
  439.  
  440.         mouse_get_last (NULL, &mp);
  441.         mp.row -= HEADER_ROWS(ps);
  442.         ctmp = screen.top_line;
  443.         if (mp.doubleclick) {
  444.             display_attachment(msgno, current->attp);
  445.         }
  446.         else {
  447.             while (mp.row && ctmp != NULL) {
  448.             --mp.row;
  449.             ctmp = ctmp->next;
  450.             }
  451.             if (ctmp != NULL)
  452.               current = ctmp;
  453.         }
  454.         }
  455.         break;
  456. #endif
  457.  
  458.       case 'w' :                /* whereis */
  459.       case ctrl('W') :
  460.       case PF12 :
  461.         /*--- get string  ---*/
  462.         {int   rc, found = 0;
  463.          char *result = NULL, buf[64];
  464.          static char last[64], tmp[64];
  465.          HelpType help;
  466.  
  467.          ps->mangled_footer = 1;
  468.          buf[0] = '\0';
  469.          sprintf(tmp, "Word to find %s%.40s%s: ",
  470.              (last[0]) ? "[" : "",
  471.              (last[0]) ? last : "",
  472.              (last[0]) ? "]" : "");
  473.          help = NO_HELP;
  474.          while(1){
  475.          rc = optionally_enter(buf,-FOOTER_ROWS(ps),0,63,1,0,
  476.                      tmp,NULL,help,0);
  477.          if(rc == 3)
  478.            help = help == NO_HELP ? h_attach_index_whereis : NO_HELP;
  479.          else if(rc == 0 || rc == 1 || !buf[0]){
  480.              if(rc == 0 && !buf[0] && last[0])
  481.                strcpy(buf, last);
  482.  
  483.              break;
  484.          }
  485.          }
  486.  
  487.          if(rc == 0 && buf[0]){
  488.          ch   = KEY_DOWN;
  489.          ctmp = current;
  490.          while(ctmp = next_attline(ctmp))
  491.            if(srchstr(ctmp->dstring, buf)){
  492.                found++;
  493.                break;
  494.            }
  495.  
  496.          if(!found){
  497.              ctmp = first_attline(current);
  498.  
  499.              while(ctmp != current)
  500.                if(srchstr(ctmp->dstring, buf)){
  501.                found++;
  502.                break;
  503.                }
  504.                else
  505.              ctmp = next_attline(ctmp);
  506.          }
  507.          }
  508.          else
  509.            result = "WhereIs cancelled";
  510.  
  511.          if(found && ctmp){
  512.          strcpy(last, buf);
  513.          result  = "Word found";
  514.          current = ctmp;
  515.          }
  516.  
  517.          q_status_message(SM_ORDER, 0, 3,
  518.                   result ? result : "Word not found");
  519.         }
  520.  
  521.         break;
  522.  
  523.       case 'a' :
  524.       case PF9 :
  525.         display_attach_info(msgno, current->attp);
  526.         break;
  527.  
  528.       case ctrl('L'):            /* redraw */
  529.           case KEY_RESIZE:
  530.         ps->mangled_screen = 1;
  531.         break;
  532.  
  533.       case 'v':                /* View command */
  534.       case ctrl('M'):
  535.       case PF4 :
  536.         display_attachment(msgno, current->attp);
  537.         break;
  538.  
  539.       case 's':                /* Save command */
  540.       case PF10 :
  541.         save_attachment(-FOOTER_ROWS(ps), msgno, current->attp);
  542.         ps->mangled_footer = 1;
  543.         break;
  544.  
  545.       case '|':                /* Pipe command */
  546.       case PF11 :
  547.         if(F_ON(F_ENABLE_PIPE, ps)){
  548.         pipe_attachment(msgno, current->attp);
  549.         ps->mangled_footer = 1;
  550.         break;
  551.         }                    /* fall thru to complain */
  552.  
  553.       default:
  554.         bogus_command(orig_ch, F_ON(F_USE_FK, ps) ? "F1" : "?");
  555.  
  556.       case NO_OP_IDLE:            /* simple timeout */
  557.       case NO_OP_COMMAND:
  558.         break;
  559.     }
  560.     }
  561.  
  562.     for(current = first_attline(current); current;){    /* clean up */
  563.     ctmp = current->next;
  564.     free_attline(¤t);
  565.     current = ctmp;
  566.     }
  567.  
  568.     ps->mangled_screen = 1;
  569.     return;
  570. }
  571.  
  572.  
  573.  
  574. /*----------------------------------------------------------------------
  575.   allocate and attach a fresh attachment line struct
  576.  
  577.   Args: current -- display line to attache new struct to
  578.  
  579.   Returns: newly alloc'd attachment display line
  580.   ----*/
  581. ATDISP_S *
  582. new_attline(current)
  583.     ATDISP_S **current;
  584. {
  585.     ATDISP_S *p;
  586.  
  587.     p = (ATDISP_S *)fs_get(sizeof(ATDISP_S));
  588.     memset((void *)p, 0, sizeof(ATDISP_S));
  589.     if(current){
  590.     if(*current){
  591.         p->next         = (*current)->next;
  592.         (*current)->next = p;
  593.         p->prev         = *current;
  594.         if(p->next)
  595.           p->next->prev = p;
  596.     }
  597.  
  598.     *current = p;
  599.     }
  600.  
  601.     return(p);
  602. }
  603.  
  604.  
  605.  
  606. /*----------------------------------------------------------------------
  607.   Release system resources associated with attachment display line
  608.  
  609.   Args: p -- line to free
  610.  
  611.   Result: 
  612.   ----*/
  613. void
  614. free_attline(p)
  615.     ATDISP_S **p;
  616. {
  617.     if(p){
  618.     if((*p)->dstring)
  619.       fs_give((void **)&(*p)->dstring);
  620.  
  621.     fs_give((void **)p);
  622.     }
  623. }
  624.  
  625.  
  626.  
  627. /*----------------------------------------------------------------------
  628.   Manage display of the attachment screen menu body.
  629.  
  630.   Args: ps -- pine struct pointer
  631.     current -- currently selected display line
  632.     screen -- reference points for display painting
  633.  
  634.   Result: 
  635.  */
  636. int
  637. attachment_screen_updater(ps, current, screen)
  638.     struct pine  *ps;
  639.     ATDISP_S     *current;
  640.     ATT_SCREEN_S *screen;
  641. {
  642.     int          dline, return_line = HEADER_ROWS(ps);
  643.     ATDISP_S *top_line, *ctmp;
  644.  
  645.     /* calculate top line of display */
  646.     dline = 0;
  647.     ctmp = top_line = first_attline(current);
  648.     do
  649.       if(((dline++)%(ps->ttyo->screen_rows-HEADER_ROWS(ps)-FOOTER_ROWS(ps)))==0)
  650.     top_line = ctmp;
  651.     while(ctmp != current && (ctmp = next_attline(ctmp)));
  652.  
  653. #ifdef _WINDOWS
  654.     /* Don't know how to manage scroll bar for attachment screen yet. */
  655.     scroll_setrange (0L);
  656. #endif
  657.  
  658.     /* mangled body or new page, force redraw */
  659.     if(ps->mangled_body || screen->top_line != top_line)
  660.       screen->current = NULL;
  661.  
  662.     /* loop thru painting what's needed */
  663.     for(dline = 0, ctmp = top_line;
  664.     dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps) - HEADER_ROWS(ps);
  665.     dline++, ctmp = next_attline(ctmp)){
  666.  
  667.     /*
  668.      * only fall thru painting if something needs painting...
  669.      */
  670.     if(!(!screen->current || ctmp == screen->current || ctmp == current))
  671.       continue;
  672.  
  673.     if(ctmp && ctmp->dstring){
  674.         char *p = tmp_20k_buf;
  675.         int   i, j, x = 0;
  676.         if(F_ON(F_FORCE_LOW_SPEED,ps) || ps->low_speed){
  677.         x = 2;
  678.         if(ctmp == current){
  679.             return_line = dline + HEADER_ROWS(ps);
  680.             PutLine0(dline + HEADER_ROWS(ps), 0, "->");
  681.         }
  682.         else
  683.           PutLine0(dline + HEADER_ROWS(ps), 0, "  ");
  684.  
  685.         /*
  686.          * Only paint lines if we have to...
  687.          */
  688.         if(screen->current)
  689.           continue;
  690.         }
  691.         else if(ctmp == current){
  692.         return_line = dline + HEADER_ROWS(ps);
  693.         StartInverse();
  694.         }
  695.  
  696.         /*
  697.          * Copy the value to a temp buffer expanding tabs, and
  698.          * making sure not to write beyond screen right...
  699.          */
  700.         for(i=0,j=x; ctmp->dstring[i] && j < ps->ttyo->screen_cols; i++){
  701.         if(ctmp->dstring[i] == ctrl('I')){
  702.             do
  703.               *p++ = ' ';
  704.             while(j < ps_global->ttyo->screen_cols && ((++j)&0x07));
  705.               
  706.         }
  707.         else{
  708.             *p++ = ctmp->dstring[i];
  709.             j++;
  710.         }
  711.         }
  712.  
  713.         *p = '\0';
  714.         PutLine0(dline + HEADER_ROWS(ps), x, tmp_20k_buf + x);
  715.  
  716.         if(ctmp == current
  717.            && !(F_ON(F_FORCE_LOW_SPEED,ps) || ps->low_speed))
  718.           EndInverse();
  719.     }
  720.     else
  721.       ClearLine(dline + HEADER_ROWS(ps));
  722.     }
  723.  
  724.     ps->mangled_body = 0;
  725.     screen->top_line = top_line;
  726.     screen->current  = current;
  727.     return(return_line);
  728. }
  729.  
  730.  
  731. /*----------------------------------------------------------------------
  732.   Redraw the attachment screen based on the global "att_screen" struct
  733.  
  734.   Args: none
  735.  
  736.   Result: 
  737.   ----*/
  738. void
  739. attachment_screen_redrawer()
  740. {
  741.     bitmap_t     bitmap;
  742.  
  743.     set_titlebar("ATTACHMENT INDEX", ps_global->mail_stream,
  744.          ps_global->context_current, ps_global->cur_folder,
  745.          ps_global->msgmap, 1, FolderName,0,0);
  746.  
  747.     ClearLine(1);
  748.  
  749.     ps_global->mangled_body = 1;
  750.     (void)attachment_screen_updater(ps_global,att_screen->current,att_screen);
  751.  
  752.     setbitmap(bitmap);
  753.     draw_keymenu(&att_index_keymenu, bitmap, ps_global->ttyo->screen_cols,
  754.          1-FOOTER_ROWS(ps_global), 0, FirstMenu,0);
  755. }
  756.  
  757.  
  758.  
  759. /*----------------------------------------------------------------------
  760.   Seek back from the given display line to the beginning of the list
  761.  
  762.   Args: p -- display linked list
  763.  
  764.   Result: 
  765.   ----*/
  766. ATDISP_S *
  767. first_attline(p)
  768.     ATDISP_S *p;
  769. {
  770.     while(p && p->prev)
  771.       p = p->prev;
  772.  
  773.     return(p);
  774. }
  775.  
  776.  
  777. int
  778. init_att_progress(msg, stream, body)
  779.     char       *msg;
  780.     MAILSTREAM *stream;
  781.     BODY    *body;
  782. {
  783.     if(save_att_length = body->size.bytes){
  784.     /* if there are display filters, factor in extra copy */
  785.     if(body->type == TYPETEXT && ps_global->VAR_DISPLAY_FILTERS)
  786.       save_att_length += body->size.bytes;
  787.  
  788.     /* if remote folder and segment not cached, factor in IMAP fetch */
  789.     if(stream && stream->mailbox && IS_REMOTE(stream->mailbox)
  790.        && !((body->type == TYPETEXT && body->contents.text)
  791.         || (body->type == TYPEMESSAGE && body->contents.msg.text)
  792.         || body->contents.binary))
  793.       save_att_length += body->size.bytes;
  794.  
  795.     gf_filter_init();            /* reset counters */
  796.     pine_gets_bytes(1);
  797.     save_att_piped(1);
  798.     return(busy_alarm(1, msg, save_att_percent, 1));
  799.     }
  800.  
  801.     return(0);
  802. }
  803.  
  804.  
  805. long
  806. save_att_piped(reset)
  807.     int reset;
  808. {
  809.     static long x;
  810.     long    y;
  811.  
  812.     if(reset){
  813.     x = y = 0L;
  814.     }
  815.     else if((y = gf_bytes_piped()) >= x){
  816.     x = y;
  817.     y = 0;
  818.     }
  819.  
  820.     return(x + y);
  821. }
  822.  
  823.  
  824. int
  825. save_att_percent()
  826. {
  827.     int i = (int) (((pine_gets_bytes(0) + save_att_piped(0)) * 100)
  828.                                / save_att_length);
  829.     return(min(i, 100));
  830. }
  831.  
  832.  
  833.  
  834. /*----------------------------------------------------------------------
  835.   Save the given attachment associated with the given message no
  836.  
  837.   Args: ps
  838.  
  839.   Result: 
  840.   ----*/
  841. void
  842. save_attachment(qline, msgno, a)
  843.      int       qline;
  844.      long      msgno;
  845.      ATTACH_S *a;
  846. {
  847.     char    filename[MAXPATH+1], full_filename[MAXPATH+1], *ill;
  848.     HelpType    help;
  849.     char       *l_string, prompt_buf[200];
  850.     int         r, is_text, over = 0, we_cancel = 0;
  851.     long        len;
  852.     PARAMETER  *param;
  853.     gf_io_t     pc;
  854.     STORE_S    *store;
  855.     char       *err;
  856.     struct variable *vars = ps_global->vars;
  857.     static ESCKEY_S att_save_opts[] = {
  858.     {ctrl('T'), 10, "^T", "To Files"},
  859.     {-1, 0, NULL, NULL},
  860.     {-1, 0, NULL, NULL},
  861.     {-1, 0, NULL, NULL}};
  862.  
  863.     is_text = a->body->type == TYPETEXT;
  864.  
  865.     /*-------  Figure out suggested file name ----*/
  866.     filename[0] = full_filename[0] = '\0';
  867.     for(param = a->body->parameter; param; param = param->next)
  868.       if(param->value && strucmp(param->attribute, "name") == 0){
  869.       strcpy(filename, param->value);
  870.       break;
  871.       }
  872.  
  873.     dprint(9, (debugfile, "save_attachment(name: %s)\n", filename));
  874.  
  875.     /*---------- Prompt the user for the file name -------------*/
  876.     r = 0;
  877. #if    !defined(DOS) && !defined(MAC) && !defined(OS2)
  878.     if(ps_global->VAR_DOWNLOAD_CMD && ps_global->VAR_DOWNLOAD_CMD[0]){
  879.     att_save_opts[++r].ch  = ctrl('V');
  880.     att_save_opts[r].rval  = 12;
  881.     att_save_opts[r].name  = "^V";
  882.     att_save_opts[r].label = "Downld Msg";
  883.     }
  884. #endif    /* !(DOS || MAC) */
  885.  
  886.     if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
  887.     att_save_opts[++r].ch  =  ctrl('I');
  888.     att_save_opts[r].rval  = 11;
  889.     att_save_opts[r].name  = "TAB";
  890.     att_save_opts[r].label = "Complete";
  891.     }
  892.  
  893.     att_save_opts[++r].ch = -1;
  894.  
  895.     help = NO_HELP;
  896.     while(1) {
  897.     sprintf(prompt_buf, "Copy attachment to file in %s directory: ",
  898.         F_ON(F_USE_CURRENT_DIR, ps_global) ? "current"
  899.         : VAR_OPER_DIR ? VAR_OPER_DIR : "home");
  900.     r = optionally_enter(filename, qline, 0, MAXPATH, 1, 0, prompt_buf,
  901.                  att_save_opts, help, 0);
  902.  
  903.         /*--- Help ----*/
  904.         if(r == 3) {
  905.             help = (help == NO_HELP) ? h_oe_export : NO_HELP;
  906.             continue;
  907.         }
  908.  
  909.     if(r == 10){                /* File Browser */
  910.         if(filename[0])
  911.           strcpy(full_filename, filename);
  912.         else if(F_ON(F_USE_CURRENT_DIR, ps_global))
  913.           (void) getcwd(full_filename, MAXPATH);
  914.         else if(VAR_OPER_DIR)
  915.           build_path(full_filename, VAR_OPER_DIR, filename);
  916.         else
  917.               build_path(full_filename, ps_global->home_dir, filename);
  918.  
  919.         r = file_lister("SAVE ATTACHMENT", full_filename, filename, TRUE,
  920.                 FB_SAVE);
  921.  
  922.         if(r == 1){
  923.         build_path (tmp_20k_buf, full_filename, filename);
  924.         strcpy (full_filename, tmp_20k_buf);
  925.         }
  926.         else
  927.           continue;
  928.     }
  929.     else if(r == 11){            /* File Name Completion */
  930.         char dir[MAXPATH], *fn;
  931.         int  l = MAXPATH;
  932.  
  933.         dir[0] = '\0';
  934.         if(*filename && (fn = last_cmpnt(filename))){
  935.         l -= fn - filename;
  936.         if(is_absolute_path(filename)){
  937.             strncpy(dir, filename, fn - filename);
  938.             dir[fn - filename] = '\0';
  939.         }
  940.         else{
  941.             char *p = NULL;
  942.             sprintf(full_filename, "%.*s", fn - filename, filename);
  943.             build_path(dir, F_ON(F_USE_CURRENT_DIR, ps_global)
  944.                        ? p = (char *) getcwd(NULL, MAXPATH)
  945.                        : VAR_OPER_DIR ? VAR_OPER_DIR
  946.                               : ps_global->home_dir,
  947.                    full_filename);
  948.             if(p)
  949.               free(p);
  950.         }
  951.         }
  952.         else{
  953.         fn = filename;
  954.         if(F_ON(F_USE_CURRENT_DIR, ps_global))
  955.           (void) getcwd(dir, MAXPATH);
  956.         else if(VAR_OPER_DIR)
  957.           strcpy(dir, VAR_OPER_DIR);
  958.         else
  959.           strcpy(dir, ps_global->home_dir);
  960.         }
  961.  
  962.         if(!pico_fncomplete(dir, fn, l - 1))
  963.           Writechar(BELL, 0);
  964.  
  965.         continue;
  966.     }
  967. #if    !defined(DOS) && !defined(MAC) && !defined(OS2)
  968.     else if(r == 12){            /* Download */
  969.         char     cmd[MAXPATH], *fp, *tfp;
  970.         int         next = 0;
  971.         PIPE_S  *syspipe;
  972.         gf_io_t  pc;
  973.  
  974.         if(ps_global->restricted){
  975.         q_status_message(SM_ORDER | SM_DING, 3, 3,
  976.                  "Download disallowed in restricted mode");
  977.         return;
  978.         }
  979.  
  980.         err = NULL;
  981.         tfp = temp_nam(NULL, "pd");
  982.         dprint(1, (debugfile, "Download attachment called!\n"));
  983.         if(store = so_get(FileStar, tfp, WRITE_ACCESS)){
  984.         sprintf(prompt_buf, "Saving to \"%.50s\"", full_filename);
  985.         we_cancel = init_att_progress(prompt_buf,
  986.                           ps_global->mail_stream,
  987.                           a->body);
  988.  
  989.         gf_set_so_writec(&pc, store);
  990.         if(err = detach(ps_global->mail_stream, msgno, a->body,
  991.                 a->number, &len, pc, NULL))
  992.           q_status_message2(SM_ORDER | SM_DING, 3, 5,
  993.                     "%s: Error writing attachment to \"%s\"",
  994.                     err, full_filename);
  995.  
  996.         /* cancel regardless, so it doesn't get in way of xfer */
  997.         cancel_busy_alarm(0);
  998.  
  999.         so_give(&store);        /* close file */
  1000.  
  1001.         if(!err){
  1002.             build_updown_cmd(cmd, ps_global->VAR_DOWNLOAD_CMD_PREFIX,
  1003.                      ps_global->VAR_DOWNLOAD_CMD, tfp);
  1004.             if(syspipe = open_system_pipe(cmd, NULL, NULL,
  1005.                           PIPE_USER | PIPE_RESET))
  1006.               (void) close_system_pipe(&syspipe);
  1007.             else
  1008.               q_status_message(SM_ORDER | SM_DING, 3, 3,
  1009.                        err = "Error running download command");
  1010.         }
  1011.  
  1012.         unlink(tfp);
  1013.         }
  1014.         else
  1015.           q_status_message(SM_ORDER | SM_DING, 3, 3,
  1016.                    err = "Error building temp file for download");
  1017.  
  1018.         fs_give((void **)&tfp);
  1019.         if(!err)
  1020.           q_status_message1(SM_ORDER, 0, 4, "Part %s downloaded",
  1021.                 a->number);
  1022.                 
  1023.  
  1024.         return;
  1025.     }
  1026. #endif    /* !(DOS || MAC) */
  1027.         else if(r == 1 || filename[0] == '\0') {
  1028.             q_status_message(SM_ORDER, 0, 2, "Save attachment cancelled");
  1029.             return;
  1030.         }
  1031.         else if(r == 4)
  1032.           continue;
  1033.  
  1034.  
  1035.         /* check out and expand file name. give possible error messages */
  1036.     if(!full_filename[0])
  1037.       strcpy(full_filename, filename);
  1038.  
  1039.         removing_trailing_white_space(full_filename);
  1040.         removing_leading_white_space(full_filename);
  1041.         if((ill = filter_filename(filename)) != NULL) {
  1042. /* BUG: we should beep when the key's pressed rather than bitch later */
  1043.             q_status_message1(SM_ORDER | SM_DING, 3, 3, "%s", ill);
  1044.             continue;
  1045.         }
  1046.  
  1047. #if    defined(DOS) || defined(OS2)
  1048.     if(is_absolute_path(full_filename)){
  1049.         fixpath(full_filename, MAXPATH);
  1050.     }
  1051. #else
  1052.     if(full_filename[0] == '~') {
  1053.         if(fnexpand(full_filename, sizeof(full_filename)) == NULL) {
  1054.         char *p = strindex(full_filename, '/');
  1055.         if(p != NULL)
  1056.           *p = '\0';
  1057.         q_status_message1(SM_ORDER | SM_DING, 3, 3,
  1058.                   "Error expanding file name: \"%s\" unknown user",
  1059.                   full_filename);
  1060.         continue;
  1061.         }
  1062.     }
  1063. #endif
  1064.  
  1065.     if(!is_absolute_path(full_filename)){
  1066.         if(F_ON(F_USE_CURRENT_DIR, ps_global))
  1067.           (void)strcpy(full_filename, filename);
  1068.         else if(VAR_OPER_DIR)
  1069.           build_path(full_filename, VAR_OPER_DIR, filename);
  1070.         else
  1071.           build_path(full_filename, ps_global->home_dir, filename);
  1072.     }
  1073.  
  1074.     break;        /* Must have got an OK file name */
  1075.     }
  1076.  
  1077.     if(ps_global->restricted) {
  1078.         q_status_message(SM_ORDER | SM_DING, 0, 4,
  1079.              "Pine demo can't save attachments");
  1080.         return;
  1081.     }
  1082.  
  1083.     if(VAR_OPER_DIR && !in_dir(VAR_OPER_DIR, full_filename)){
  1084.     q_status_message1(SM_ORDER, 0, 2, "Can't save to file outside of %s",
  1085.               VAR_OPER_DIR);
  1086.     return;
  1087.     }
  1088.      
  1089.  
  1090.     /*----------- Write the contents to the file -------------*/
  1091.     if(can_access(full_filename, ACCESS_EXISTS) == 0) {
  1092.     static ESCKEY_S access_opts[] = {
  1093.         {'o', 'o', "O", "Overwrite"},
  1094.         {'a', 'a', "A", "Append"},
  1095.         {-1, 0, NULL, NULL}};
  1096.  
  1097.         sprintf(prompt_buf,
  1098.         "File \"%s%s\" already exists.  Overwrite or append it ? ",
  1099.         ((r = strlen(full_filename)) > 20) ? "..." : "",
  1100.                 full_filename + ((r > 20) ? r - 20 : 0));
  1101.  
  1102.     switch(radio_buttons(prompt_buf, -FOOTER_ROWS(ps_global),
  1103.                  access_opts, 'a', 'x', NO_HELP, RB_NORM)){
  1104.       case 'o' :
  1105.         over = 1;
  1106.         if(unlink(full_filename) < 0){    /* BUG: breaks links */
  1107.         q_status_message2(SM_ORDER | SM_DING, 3, 5,
  1108.                   "Error deleting old %s: %s",
  1109.                   full_filename, error_description(errno));
  1110.         return;
  1111.         }
  1112.  
  1113.         break;
  1114.  
  1115.       case 'a' :
  1116.         over = -1;
  1117.         break;
  1118.  
  1119.       case 'x' :
  1120.       default :
  1121.             q_status_message(SM_ORDER, 0, 2, "Save of attachment cancelled");
  1122.             return;
  1123.     }
  1124.     }
  1125.  
  1126.     if((store = so_get(FileStar, full_filename, WRITE_ACCESS)) == NULL){
  1127.     q_status_message2(SM_ORDER | SM_DING, 3, 5,
  1128.               "Error opening destination %s: %s",
  1129.               full_filename, error_description(errno));
  1130.     return;
  1131.     }
  1132.  
  1133.     sprintf(prompt_buf, "Saving to \"%.50s\"", full_filename);
  1134.     we_cancel = init_att_progress(prompt_buf, ps_global->mail_stream, a->body);
  1135.  
  1136.     gf_set_so_writec(&pc, store);
  1137.     err = detach(ps_global->mail_stream,msgno,a->body,a->number,&len,pc,NULL);
  1138.  
  1139.     if(we_cancel)
  1140.       cancel_busy_alarm(0);
  1141.  
  1142.     if(!err){
  1143.         l_string = cpystr(byte_string(len));
  1144.         q_status_message8(SM_ORDER, 0, 4,
  1145.               "Part %s, %s%s %s to \"%s\"%s%s%s",
  1146.               a->number, 
  1147.               is_text
  1148.                 ? comatose(a->body->size.lines) : l_string,
  1149.               is_text ? " lines" : "",
  1150.               over==0 ? "written"
  1151.                   : over==1 ? "overwritten" : "appended",
  1152.               full_filename,
  1153.               (is_text || len == a->body->size.bytes)
  1154.                 ? "" : "(decoded from ",
  1155.                           (is_text || len == a->body->size.bytes)
  1156.                 ? "" : byte_string(a->body->size.bytes),
  1157.               is_text || len == a->body->size.bytes
  1158.                 ? "" : ")");
  1159.         fs_give((void **)&l_string);
  1160.     }
  1161.     else
  1162.       q_status_message2(SM_ORDER | SM_DING, 3, 5,
  1163.             "%s: Error writing attachment to \"%s\"",
  1164.                         err, full_filename);
  1165.     so_give(&store);
  1166. }
  1167.  
  1168.  
  1169. /*----------------------------------------------------------------------
  1170.   Unpack and display the given attachment associated with given message no.
  1171.  
  1172.   Args: msgno -- message no attachment is part of
  1173.     a -- attachment to display
  1174.  
  1175.   Returns: 0 on success, non-zero (and error message queued) otherwise
  1176.   ----*/        
  1177. int
  1178. display_attachment(msgno, a)
  1179.      long      msgno;
  1180.      ATTACH_S *a;
  1181. {
  1182.     char    *filename;
  1183.     STORE_S *store;
  1184.     gf_io_t  pc;
  1185.     char    *err;
  1186.     int      we_cancel = 0;
  1187. #if !defined(DOS) && !defined(OS2)
  1188.     char     prefix[8];
  1189. #endif
  1190.  
  1191.     if(a->can_display == CD_DEFERRED){
  1192.     int use_viewer;
  1193.     a->can_display = mime_can_display(a->body->type, a->body->subtype,
  1194.                       a->body->parameter, &use_viewer)
  1195.               ? CD_GOFORIT : CD_NOCANDO;
  1196.     a->use_external_viewer = use_viewer;
  1197.     }
  1198.  
  1199.     /*------- Display the attachment -------*/
  1200.     if(a->can_display == CD_NOCANDO) {
  1201.         /*----- Can't display this type ------*/
  1202.     if(a->body->encoding < ENCOTHER)
  1203.       q_status_message3(SM_ORDER | SM_DING, 3, 5,
  1204.              "Don't know how to display %s%s%s attachments. Try Save.",
  1205.                 body_type_names(a->body->type),
  1206.                 a->body->subtype ? "/" : "",
  1207.                 a->body->subtype ? a->body->subtype :"");
  1208.     else
  1209.       q_status_message1(SM_ORDER | SM_DING, 3, 5,
  1210.                 "Don't know how to unpack \"%s\" encoding",
  1211.                 body_encodings[(a->body->encoding <= ENCMAX)
  1212.                          ? a->body->encoding : ENCOTHER]);
  1213.  
  1214.         return(1);
  1215.     }
  1216.     else if(!a->use_external_viewer){
  1217.     if(a->body->type == TYPETEXT)
  1218.       display_text_att(msgno, a);
  1219.     else if(a->body->type == TYPEMESSAGE)
  1220.       display_msg_att(msgno, a);
  1221.     else if(a->body->type == TYPEAPPLICATION
  1222.         && a->body->subtype
  1223.         && !strucmp(a->body->subtype, "DIRECTORY"))
  1224.       display_abook_att(msgno, a);
  1225.  
  1226.     ps_global->mangled_screen = 1;
  1227.     return(0);
  1228.     }
  1229.  
  1230.     /*------ Write the image to a temporary file ------*/
  1231. #if defined(DOS) || defined(OS2)
  1232.     /*
  1233.      *  Dos applications are particular about the file name extensions
  1234.      *  Try to look up the expected extension from the mime.types file.
  1235.      */
  1236.     {  char ext[32];
  1237.        char mtype[128];
  1238.        
  1239.      ext[0] = '\0';
  1240.      strcpy (mtype, body_type_names(a->body->type));
  1241.      if (a->body->subtype) {
  1242.      strcat (mtype, "/");
  1243.      strcat (mtype, a->body->subtype);
  1244.      }
  1245.  
  1246.      set_mime_extension_by_type (ext, mtype);
  1247.      filename = temp_nam_ext(NULL, "im", ext);
  1248.    }
  1249. #else
  1250.     sprintf(prefix, "img-%.3s", (a->body->subtype) ? a->body->subtype : "unk");
  1251.     filename = temp_nam(NULL, prefix);
  1252. #endif
  1253.  
  1254.     if((store = so_get(FileStar, filename, WRITE_ACCESS)) == NULL){
  1255.         q_status_message2(SM_ORDER | SM_DING, 3, 5,
  1256.                           "Error \"%s\", Can't write file %s",
  1257.                           error_description(errno), filename);
  1258.         return(1);
  1259.     }
  1260.  
  1261.  
  1262.     if(a->body->size.bytes){
  1263.     char msg_buf[128];
  1264.  
  1265.     sprintf(msg_buf, "Decoding %s%.50s%s%s",
  1266.         a->description ? "\"" : "",
  1267.         a->description ? a->description : "attachment number ",
  1268.         a->description ? "" : a->number,
  1269.         a->description ? "\"" : "");
  1270.     we_cancel = init_att_progress(msg_buf, ps_global->mail_stream,
  1271.                       a->body);
  1272.     }
  1273.  
  1274.     gf_set_so_writec(&pc, store);
  1275.     err = detach(ps_global->mail_stream,msgno,a->body,a->number,NULL,pc,NULL);
  1276.  
  1277.     if(we_cancel)
  1278.       cancel_busy_alarm(0);
  1279.  
  1280.     if(err){
  1281.     q_status_message2(SM_ORDER | SM_DING, 3, 5,
  1282.              "%s: Error saving image to temp file %s", err, filename);
  1283.     return(1);
  1284.     }
  1285.  
  1286.     so_give(&store);
  1287.  
  1288.     /*----- Run the viewer process ----*/
  1289.     run_viewer(filename, a->body);
  1290.     fs_give((void **)&filename);
  1291.     return(0);
  1292. }
  1293.  
  1294.  
  1295. /*----------------------------------------------------------------------
  1296.    Fish the required command from mailcap and run it
  1297.  
  1298.   Args: image_file -- The name of the file to pass viewer
  1299.     body -- body struct containing type/subtype of part
  1300.  
  1301. A side effect may be that scrolltool is called as well if
  1302. exec_mailcap_cmd has any substantial output...
  1303.  ----*/
  1304. void
  1305. run_viewer(image_file, body)
  1306.      char *image_file;
  1307.      BODY *body;
  1308. {
  1309.     char *cmd            = NULL;
  1310.     int   needs_terminal = 0, we_cancel = 0;
  1311.  
  1312.     we_cancel = busy_alarm(1, "Displaying attachment", NULL, 1);
  1313.  
  1314.     if(cmd = mailcap_build_command(body, image_file, &needs_terminal)){
  1315.     if(we_cancel)
  1316.       cancel_busy_alarm(-1);
  1317.  
  1318.     exec_mailcap_cmd(cmd, image_file, needs_terminal);
  1319.     fs_give((void **)&cmd);
  1320.     }
  1321.     else{
  1322.     if(we_cancel)
  1323.       cancel_busy_alarm(-1);
  1324.  
  1325.     q_status_message1(SM_ORDER, 3, 4, "Cannot display %s attachment",
  1326.               type_desc(body->type, body->subtype,
  1327.                     body->parameter, 1));
  1328.     }
  1329. }
  1330.  
  1331.  
  1332.  
  1333. /*----------------------------------------------------------------------
  1334.   Detach and provide for browsing a text body part
  1335.  
  1336.   Args: msgno -- raw message number to get part from
  1337.      a -- attachment struct for the desired part
  1338.  
  1339.   Result: 
  1340.  ----*/
  1341. void
  1342. display_text_att(msgno, a)
  1343.     long      msgno;
  1344.     ATTACH_S *a;
  1345. {
  1346.     STORE_S        *store;
  1347.     gf_io_t         pc;
  1348.     SourceType        src = CharStar;
  1349.  
  1350.     clear_index_cache_ent(msgno);
  1351.  
  1352.     /* BUG, should check this return code */
  1353.     (void)mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
  1354.  
  1355.     /* initialize a storage object */
  1356. #if    defined(DOS) && !defined(WIN32)
  1357.     if(a->body->size.bytes > MAX_MSG_INCORE
  1358.        || strcmp(ps_global->mail_stream->dtb->name, "nntp") == 0)
  1359.       src = FileStar;
  1360. #endif
  1361.  
  1362.     if(store = so_get(src, NULL, EDIT_ACCESS)){
  1363.     gf_set_so_writec(&pc, store);
  1364.     (void) decode_text(a, msgno, pc, QStatus, 1);
  1365.     scrolltool(so_text(store), "ATTACHED TEXT", AttachText, src, a);
  1366.     so_give(&store);    /* free resources associated with store */
  1367.     }
  1368.     else
  1369.       q_status_message(SM_ORDER | SM_DING, 3, 3,
  1370.                "Error allocating space for attachment.");
  1371. }
  1372.  
  1373.  
  1374. /*----------------------------------------------------------------------
  1375.   Detach and provide for browsing a body part of type "MESSAGE"
  1376.  
  1377.   Args: msgno -- message number to get partrom
  1378.      a -- attachment struct for the desired part
  1379.  
  1380.   Result: 
  1381.  ----*/
  1382. void
  1383. display_msg_att(msgno, a)
  1384.     long      msgno;
  1385.     ATTACH_S *a;
  1386. {
  1387.     STORE_S        *store;
  1388.     gf_io_t         pc;
  1389.     SourceType        src = CharStar;
  1390.  
  1391.     clear_index_cache_ent(msgno);
  1392.  
  1393.     /* BUG, should check this return code */
  1394.     (void)mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
  1395.  
  1396.     /* initialize a storage object */
  1397. #if    defined(DOS) && !defined(WIN32)
  1398.     if(a->body->size.bytes > MAX_MSG_INCORE
  1399.        || strcmp(ps_global->mail_stream->dtb->name, "nntp") == 0)
  1400.       src = FileStar;
  1401. #endif
  1402.  
  1403.     if(!(store = so_get(src, NULL, EDIT_ACCESS))){
  1404.     q_status_message(SM_ORDER | SM_DING, 3, 3,
  1405.              "Error allocating space for message.");
  1406.     return;
  1407.     }
  1408.  
  1409.     gf_set_so_writec(&pc, store);
  1410.  
  1411.     if(a->body->subtype && strucmp(a->body->subtype, "rfc822") == 0){
  1412.     format_envelope(NULL, 0L, a->body->contents.msg.env, pc,
  1413.             FE_DEFAULT, NULL);
  1414.     gf_puts(NEWLINE, pc);
  1415.     if((a+1)->description && (a+1)->body && (a+1)->body->type == TYPETEXT){
  1416.         (void) decode_text(a+1, msgno, pc, QStatus, 1);
  1417.     }
  1418.     else{
  1419.         gf_puts("[Can't display ", pc);
  1420.         if((a+1)->description && (a+1)->body)
  1421.           gf_puts("first non-", pc);
  1422.         else
  1423.           gf_puts("missing ", pc);
  1424.  
  1425.         gf_puts("text segment]", pc);
  1426.     }
  1427.     }
  1428.     else if(a->body->subtype 
  1429.         && strucmp(a->body->subtype, "external-body") == 0) {
  1430.     gf_puts("This part is not included and can be fetched as follows:",pc);
  1431.     gf_puts(NEWLINE, pc);
  1432.     gf_puts(NEWLINE, pc);
  1433.     gf_puts(display_parameters(a->body->parameter), pc);
  1434.     }
  1435.     else
  1436.       (void) decode_text(a, msgno, pc, QStatus, 1);
  1437.  
  1438.     scrolltool(so_text(store), "ATTACHED MESSAGE", AttachText, src, a);
  1439.  
  1440.     so_give(&store);    /* free resources associated with store */
  1441. }
  1442.  
  1443.  
  1444. void
  1445. display_abook_att(msgno, a)
  1446.     long      msgno;
  1447.     ATTACH_S *a;
  1448. {
  1449.     STORE_S   *store;
  1450.     char     **lines, **ll, *defaulttype = NULL;
  1451.     PARAMETER *parm;
  1452.  
  1453.     lines = detach_abook_att(ps_global->mail_stream, msgno, a->body, a->number);
  1454.     if(!lines){
  1455.     q_status_message(SM_ORDER | SM_DING, 3, 3,
  1456.              "Error accessing attachment."); 
  1457.     return;
  1458.     }
  1459.  
  1460.     if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
  1461.     if(lines)
  1462.       free_list(&lines);
  1463.  
  1464.     q_status_message(SM_ORDER | SM_DING, 3, 3,
  1465.              "Error allocating space for attachment."); 
  1466.     return;
  1467.     }
  1468.  
  1469.     for(parm = a->body->parameter; parm; parm = parm->next)
  1470.       if(!strucmp("DEFAULTTYPE", parm->attribute))
  1471.     break;
  1472.     
  1473.     if(parm)
  1474.       defaulttype = parm->value;
  1475.  
  1476.     for(ll = lines; ll && *ll && **ll; ll++){
  1477.     char *p;
  1478.  
  1479.     if(ll == lines)
  1480.       so_puts(store, "  ");
  1481.     else
  1482.       so_puts(store, "\n  ");
  1483.  
  1484.     p = (char *)rfc1522_decode((unsigned char *)tmp_20k_buf, *ll, NULL);
  1485.     if(p && *p == ':' && defaulttype){
  1486.         unsigned char buf[1000];
  1487.  
  1488.         so_puts(store, (char *)rfc1522_decode(buf, defaulttype, NULL));
  1489.     }
  1490.  
  1491.     so_puts(store, p);
  1492.     }
  1493.  
  1494.     so_puts(store, "\n\n(This is an address book entry which has been forwarded to you.\nYou may add it to your address book by exiting this screen and the\nattachment index screen, then using the \"TakeAddr\" command.  You will have\na chance to edit it before committing it to your address book.)\n");
  1495.  
  1496.     scrolltool(so_text(store), "ADDRESS BOOK ATTACHMENT", ViewAbookAtt,
  1497.            CharStar, NULL);
  1498.     so_give(&store);
  1499.  
  1500.     if(lines)
  1501.       free_list(&lines);
  1502. }
  1503.  
  1504.  
  1505.  
  1506. /*----------------------------------------------------------------------
  1507.   Display attachment information
  1508.  
  1509.   Args: msgno -- message number to get partrom
  1510.      a -- attachment struct for the desired part
  1511.  
  1512.   Result: a screen containing information about attachment:
  1513.       Type        :
  1514.       Subtype    :
  1515.       Parameters    :
  1516.         Comment    :
  1517.             FileName    :
  1518.       Encoded size    :
  1519.       Viewer    :
  1520.  ----*/
  1521. void
  1522. display_attach_info(msgno, a)
  1523.     long      msgno;
  1524.     ATTACH_S *a;
  1525. {
  1526.     int           i;
  1527.     STORE_S   *store;
  1528.     PARAMETER *parms;
  1529.  
  1530.     if(a->can_display == CD_DEFERRED){
  1531.     int use_viewer;
  1532.     a->can_display = mime_can_display(a->body->type, a->body->subtype,
  1533.                       a->body->parameter, &use_viewer)
  1534.               ? CD_GOFORIT : CD_NOCANDO;
  1535.     a->use_external_viewer = use_viewer;
  1536.     }
  1537.  
  1538.     if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
  1539.     q_status_message(SM_ORDER | SM_DING, 3, 3,
  1540.              "Error allocating space for message.");
  1541.     return;
  1542.     }
  1543.  
  1544.     so_puts(store, "\nDetails about Attachment #");
  1545.     so_puts(store, a->number);
  1546.     so_puts(store, " :\n\n\n");
  1547.     so_puts(store, "\t\tType\t\t: ");
  1548.     so_puts(store, body_type_names(a->body->type));
  1549.     so_puts(store, "\n");
  1550.     so_puts(store, "\t\tSubtype\t\t: ");
  1551.     so_puts(store, a->body->subtype ? a->body->subtype : "Unknown");
  1552.     so_puts(store, "\n");
  1553.     so_puts(store, "\t\tEncoding\t: ");
  1554.     so_puts(store, a->body->encoding < ENCMAX
  1555.              ? body_encodings[a->body->encoding]
  1556.              : "Unknown");
  1557.     so_puts(store, "\n");
  1558.     if(a->body->parameter){
  1559.     so_puts(store, "\t\tParameters\t: ");
  1560.     for(i=0, parms=a->body->parameter; parms; parms = parms->next, i++){
  1561.         so_puts(store, parms->attribute);
  1562.         so_puts(store, " = ");
  1563.         so_puts(store, parms->value);
  1564.         so_puts(store, "\n");
  1565.         if(parms->next)
  1566.           so_puts(store, "\t\t\t\t  ");
  1567.     }
  1568.     }
  1569.  
  1570.     if(a->body->description){
  1571.     so_puts(store, "\t\tDescription\t: \"");
  1572.     so_puts(store, a->body->description);
  1573.     so_puts(store, "\"\n");
  1574.     /* BUG: Wrap description text if necessary */
  1575.     }
  1576.  
  1577.     so_puts(store, "\t\tApprox. Size\t: ");
  1578.     so_puts(store, comatose((a->body->encoding == ENCBASE64)
  1579.                   ? ((a->body->size.bytes * 3)/4)
  1580.                   : a->body->size.bytes));
  1581.     so_puts(store, " bytes\n");
  1582.     so_puts(store, "\t\tDisplay Method\t: ");
  1583.     if(a->can_display == CD_NOCANDO) {
  1584.     so_puts(store, "Can't, ");
  1585.     so_puts(store, (a->body->encoding < ENCOTHER)
  1586.              ? "Unknown Attachment Format"
  1587.              : "Unknown Encoding");
  1588.     }
  1589.     else if(!a->use_external_viewer){
  1590.     so_puts(store, "Pine's Internal Pager");
  1591.     }
  1592.     else{
  1593.     int   nt;
  1594.     char *cmd;
  1595.  
  1596.     if(cmd = mailcap_build_command(a->body, "<datafile>", &nt)){
  1597.         so_puts(store, "\"");
  1598.         so_puts(store, cmd);
  1599.         so_puts(store, "\"");
  1600.         fs_give((void **)&cmd);
  1601.     }
  1602.     }
  1603.  
  1604.     so_puts(store, "\n");
  1605.  
  1606.     scrolltool(so_text(store), "ABOUT ATTACHMENT", AttachText, CharStar, NULL);
  1607.     so_give(&store);    /* free resources associated with store */
  1608.     ps_global->mangled_screen = 1;
  1609. }
  1610.  
  1611.  
  1612.  
  1613. /*----------------------------------------------------------------------
  1614.  
  1615.   ----*/        
  1616. void
  1617. pipe_attachment(msgno, a)
  1618.      long      msgno;
  1619.      ATTACH_S *a;
  1620. {
  1621.     char    *err, *resultfilename = NULL, prompt[80];
  1622.     int      rc, flags, capture = 1, raw = 0, we_cancel = 0;
  1623.     PIPE_S  *syspipe;
  1624.     HelpType help;
  1625.     char     pipe_command[MAXPATH+1];
  1626.     static ESCKEY_S pipe_opt[] = {
  1627.     {0, 0, "", ""},
  1628.     {ctrl('W'), 10, "^W", NULL},
  1629.     {ctrl('Y'), 11, "^Y", NULL},
  1630.     {-1, 0, NULL, NULL}
  1631.     };
  1632.     
  1633.     if(ps_global->restricted){
  1634.     q_status_message(SM_ORDER | SM_DING, 0, 4,
  1635.              "Pine demo can't pipe attachments");
  1636.     return;
  1637.     }
  1638.  
  1639.     help = NO_HELP;
  1640.     pipe_command[0] = '\0';
  1641.     while(1){
  1642.     sprintf(prompt, "Pipe %sattachment %s to %s: ", raw ? "RAW " : "",
  1643.         a->number, capture ? "" : "(Free Output) ");
  1644.     pipe_opt[1].label = raw ? "DecodedData" : "Raw Data";
  1645.     pipe_opt[2].label = capture ? "Free Output" : "Capture Output";
  1646.     rc = optionally_enter(pipe_command, -FOOTER_ROWS(ps_global), 0,
  1647.                   MAXPATH, 1, 0, prompt, pipe_opt, help, 0);
  1648.     if(rc == -1){
  1649.         q_status_message(SM_ORDER | SM_DING, 3, 4,
  1650.                  "Internal problem encountered");
  1651.         break;
  1652.     }
  1653.     else if(rc == 10){
  1654.         raw = !raw;            /* flip raw text */
  1655.     }
  1656.     else if(rc == 11){
  1657.         capture = !capture;        /* flip capture output */
  1658.     }
  1659.     else if(rc == 0){
  1660.         if(pipe_command[0] == '\0'){
  1661.         q_status_message(SM_ORDER, 0, 2, "Pipe command cancelled");
  1662.         break;
  1663.         }
  1664.  
  1665.         flags = PIPE_USER | PIPE_WRITE | PIPE_STDERR;
  1666.         if(!capture){
  1667. #ifndef    _WINDOWS
  1668.         ClearScreen();
  1669.         fflush(stdout);
  1670.         clear_cursor_pos();
  1671.         ps_global->mangled_screen = 1;
  1672. #endif
  1673.         flags |= PIPE_RESET;
  1674.         }
  1675.  
  1676.         if(syspipe = open_system_pipe(pipe_command,
  1677.                    (flags&PIPE_RESET) ? NULL : &resultfilename,
  1678.                    NULL, flags)){
  1679.         gf_io_t  pc;        /* wire up a generic putchar */
  1680.         gf_set_writec(&pc, syspipe->out.f, 0L, FileStar);
  1681.  
  1682.         /*------ Write the image to a temporary file ------*/
  1683.         if(raw){        /* pipe raw text */
  1684.             char      *contents;
  1685.             unsigned long len;
  1686.             gf_io_t    gc;
  1687.             SourceType src = CharStar;
  1688. #if    defined(DOS) && !defined(WIN32)
  1689.             char *tmpfile_name = NULL;
  1690. #endif
  1691.  
  1692.             err = NULL;
  1693. #if    defined(DOS) && !defined(WIN32)
  1694.             if(a->body->size.bytes > MAX_MSG_INCORE
  1695.                || !strcmp(ps_global->mail_stream->dtb->name,"nntp")){
  1696.             src = FileStar;
  1697.             if(!(tmpfile_name = temp_nam(NULL, "dt"))
  1698.                || !(append_file = fopen(tmpfile_name, "w+b"))){
  1699.                 if(tmpfile_name)
  1700.                   fs_give((void **)&tmpfile_name);
  1701.  
  1702.                 err = "Can't create temp file for pipe";
  1703.             }
  1704.             else
  1705.               mail_parameters(ps_global->mail_stream, SET_GETS,
  1706.                       (void *)dos_gets);
  1707.             }
  1708.             else
  1709.               mail_parameters(ps_global->mail_stream, SET_GETS,
  1710.                       (void *)NULL);
  1711. #endif    /* DOS */
  1712.  
  1713.             if(capture)
  1714.               we_cancel = busy_alarm(1, NULL, NULL, 0);
  1715.             else
  1716.               suspend_busy_alarm();
  1717.  
  1718.             if(!err
  1719.                && !(contents = mail_fetchbody(ps_global->mail_stream,
  1720.                               msgno, a->number, &len)))
  1721.               err = "Can't access body part";
  1722.  
  1723.             if(!err){
  1724.             gf_set_readc(&gc,
  1725. #if    defined(DOS) && !defined(WIN32)
  1726.                      (src == FileStar) ? (void *)append_file :
  1727. #endif
  1728.                      contents, len, src);
  1729.             gf_filter_init();
  1730.             gf_link_filter(gf_nvtnl_local);
  1731.             err = gf_pipe(gc, pc);
  1732.             }
  1733.  
  1734. #if    defined(DOS) && !defined(WIN32)
  1735.             /*
  1736.              * free up file pointer, and delete tmpfile
  1737.              */
  1738.             if(src == FileStar){
  1739.             fclose(append_file);
  1740.             append_file = NULL;
  1741.             unlink(tmpfile_name);
  1742.             fs_give((void **)&tmpfile_name);
  1743.             mail_parameters(ps_global->mail_stream, SET_GETS,
  1744.                     (void *)NULL);
  1745.             mail_gc(ps_global->mail_stream, GC_TEXTS);
  1746.             }
  1747. #endif
  1748.             if(capture){
  1749.             if(we_cancel)
  1750.               cancel_busy_alarm(0);
  1751.             }
  1752.             else
  1753.               resume_busy_alarm();
  1754.         }
  1755.         else{
  1756.             /* BUG: there's got to be a better way */
  1757.             if(!capture)
  1758.               ps_global->print = (PRINT_S *) 1;
  1759.  
  1760.             err = detach(ps_global->mail_stream, msgno, a->body,
  1761.                  a->number, (long *)NULL, pc, NULL);
  1762.             ps_global->print = (PRINT_S *) NULL;
  1763.         }
  1764.  
  1765.         if(err)
  1766.           q_status_message1(SM_ORDER | SM_DING, 3, 4,
  1767.                     "Error detaching for pipe: %s", err);
  1768.  
  1769.         (void) close_system_pipe(&syspipe);
  1770.         if(err)
  1771.           unlink(resultfilename);
  1772.         else
  1773.           display_output_file(resultfilename,"PIPE ATTACHMENT", NULL);
  1774.  
  1775.         fs_give((void **)&resultfilename);
  1776.         }
  1777.         else
  1778.           q_status_message(SM_ORDER | SM_DING, 3, 4,
  1779.                    "Error opening pipe");
  1780.  
  1781.         break;
  1782.     }
  1783.     else if(rc == 1){
  1784.         q_status_message(SM_ORDER, 0, 2,"Pipe cancelled");
  1785.         break;
  1786.     }
  1787.     else if(rc = 3)
  1788.       help = (help == NO_HELP) ? h_pipe_attach : NO_HELP;
  1789.     }
  1790. }
  1791.  
  1792.  
  1793. /*
  1794.  * We need to defined simple functions here for the piping and 
  1795.  * temporary storage object below.  We can use the filter.c functions
  1796.  * because they're already in use for the "putchar" function passed to
  1797.  * detach.
  1798.  */
  1799. static STORE_S *detach_so = NULL;
  1800.  
  1801. /*
  1802.  * The display filter is locally global because it's set in df_trigger_cmp
  1803.  * which sniffs at lines of the unencoded segment...
  1804.  */
  1805. static char    *display_filter;
  1806. static struct   triggers {
  1807.     int           (*cmp) PROTO((char *, char *));
  1808.     char        *text;
  1809.     char        *cmd;
  1810.     struct triggers *next;
  1811. } *df_trigger_list;
  1812. struct triggers *build_trigger_list PROTO(());
  1813. void         blast_trigger_list PROTO((struct triggers **));
  1814.  
  1815.  
  1816.  
  1817. int
  1818. detach_writec(c)
  1819.     int c;
  1820. {
  1821.     return(so_writec(c, detach_so));
  1822. }
  1823.  
  1824.  
  1825. /*----------------------------------------------------------------------
  1826.   detach the given body part using the given encoding
  1827.  
  1828.   Args: a bunch
  1829.  
  1830.   Returns: NULL on success, error message otherwise
  1831.   ----*/
  1832. char *
  1833. detach(stream, msg_no, body, part_no, len, pc, aux_filters)
  1834.      MAILSTREAM *stream;        /* c-client stream to use         */
  1835.      long        msg_no;        /* message number to deal with      */
  1836.      BODY       *body;            /* body pointer           */
  1837.      char       *part_no;        /* part number of message       */
  1838.      long       *len;            /* returns bytes read in this arg */
  1839.      gf_io_t     pc;            /* where to put it          */
  1840.      filter_t   *aux_filters;        /* null terminated array of filts */
  1841. {
  1842.     unsigned long  length, rv;
  1843.     int            we_cancel = 0;
  1844.     char          *status, *contents, *fcontents = NULL;
  1845.     gf_io_t        gc;
  1846.     SourceType     src = CharStar;
  1847.     static char    err_string[100];
  1848. #ifdef    DOS
  1849. #ifndef    WIN32
  1850.     char      *tmpfile_name = NULL;
  1851. #endif
  1852.     extern unsigned char  *xlate_to_codepage;
  1853. #endif
  1854.  
  1855.     err_string[0] = '\0';
  1856.  
  1857. #if    defined(DOS) && !defined(WIN32)
  1858.     if(body->size.bytes > MAX_MSG_INCORE || !strcmp(stream->dtb->name,"nntp")){
  1859.     src = FileStar;
  1860.     if(!(tmpfile_name = temp_nam(NULL, "dt"))
  1861.        || !(append_file = fopen(tmpfile_name, "w+b"))){
  1862.         if(tmpfile_name)
  1863.           fs_give((void **)&tmpfile_name);
  1864.  
  1865.         sprintf(err_string, "Can't detach!  Temp file %s",
  1866.             error_description(errno));
  1867.         return(err_string);
  1868.     }
  1869.  
  1870.     mail_parameters(stream, SET_GETS, (void *)dos_gets);
  1871.     }
  1872.     else
  1873.       mail_parameters(stream, SET_GETS, (void *)NULL);
  1874. #endif    /* DOS */
  1875.  
  1876.     if(!ps_global->print)
  1877.       we_cancel = busy_alarm(1, NULL, NULL, 0);
  1878.  
  1879.     gf_filter_init();            /* prepare for filtering! */
  1880.  
  1881.     /*
  1882.      * go grab the requested body part
  1883.      */
  1884.     contents = mail_fetchbody(stream, msg_no, part_no, &length);
  1885.     if(contents == NULL) {
  1886.     sprintf(err_string, "Unable to access body part %s", part_no);
  1887.     rv = 0L;
  1888.     goto fini;
  1889.     }
  1890.  
  1891.     rv = (length) ? length : 1L;
  1892.  
  1893.     switch(body->encoding) {        /* handle decoding */
  1894.       case ENC7BIT:
  1895.       case ENC8BIT:
  1896.       case ENCBINARY:
  1897.         break;
  1898.  
  1899.       case ENCBASE64:
  1900.     gf_link_filter(gf_b64_binary);
  1901.         break;
  1902.  
  1903.       case ENCQUOTEDPRINTABLE:
  1904.     gf_link_filter(gf_qp_8bit);
  1905.         break;
  1906.  
  1907.       case ENCOTHER:
  1908.       default:
  1909.     dprint(1, (debugfile, "detach: unknown CTE: \"%s\" (%d)\n",
  1910.            (body->encoding <= ENCMAX) ? body_encodings[body->encoding]
  1911.                           : "BEYOND-KNOWN-TYPES",
  1912.            body->encoding));
  1913.     break;
  1914.     }
  1915.  
  1916.     /*
  1917.      * If we're detaching a text segment and there are user-defined
  1918.      * filters and there are text triggers to look for, install filter
  1919.      * to let us look at each line...
  1920.      */
  1921.     display_filter = NULL;
  1922.     if(body->type == TYPETEXT && ps_global->VAR_DISPLAY_FILTERS){
  1923.     /* check for "static" triggers (i.e., none or CHARSET) */
  1924.     if(!df_static_trigger(body)
  1925.        && (df_trigger_list = build_trigger_list())){
  1926.         /* else look for matching text trigger */
  1927.         gf_line_test_opt(df_trigger_cmp);
  1928.         gf_link_filter(gf_line_test);
  1929.     }
  1930.     }
  1931.     else
  1932.       /* add aux filters if we're not going to MIME decode into a temporary
  1933.        * storage object, otherwise we pass the aux_filters on to gf_filter
  1934.        * below so it can pass what comes out of the external filter command
  1935.        * thru the rest of the filters...
  1936.        */
  1937.       while(aux_filters && *aux_filters)    /* apply provided filters */
  1938.     gf_link_filter(*aux_filters++);
  1939.  
  1940.     /*
  1941.      * Following canonical model, after decoding convert newlines from
  1942.      * crlf to local convention.
  1943.      */
  1944.     if(body->type == TYPETEXT || (body->type == TYPEMESSAGE
  1945.                   && !strucmp(body->subtype, "rfc822"))){
  1946.     gf_link_filter(gf_nvtnl_local);
  1947. #ifdef    DOS
  1948.     /*
  1949.      * When detaching a text part AND it's US-ASCII OR it
  1950.      * matches what the user's defined as our charset,
  1951.      * translate it...
  1952.      */
  1953.     if(mime_can_display(body->type, body->subtype, body->parameter,
  1954.                                 (int *)NULL)
  1955.        && xlate_to_codepage){
  1956.         gf_translate_opt(xlate_to_codepage, 256);
  1957.         gf_link_filter(gf_translate);
  1958.     }
  1959. #endif
  1960.     }
  1961.  
  1962. #ifdef    LATER
  1963.     dprint(9, (debugfile, "Attachment lengths: %ld (decoded) %lud (encoded)\n",
  1964.                *decoded_len, length));
  1965. #endif
  1966.  
  1967.     gf_set_readc(&gc,
  1968. #if    defined(DOS) && !defined(WIN32)
  1969.          /*
  1970.           * If we fetched this to a file, make sure we set
  1971.           * up the storage object's getchar function accordingly...
  1972.           */
  1973.          (src == FileStar) ? (void *)append_file :
  1974. #endif
  1975.          contents, length, src);
  1976.  
  1977.     /*
  1978.      * If we're detaching a text segment and a user-defined filter may
  1979.      * need to be invoked later (see below), decode the segment into
  1980.      * a temporary storage object...
  1981.      */
  1982.     if(body->type == TYPETEXT && ps_global->VAR_DISPLAY_FILTERS
  1983.        && !(detach_so = so_get(src, NULL, EDIT_ACCESS)))
  1984.       strcpy(err_string,
  1985.        "Formatting error: no space to make copy, no display filters used");
  1986.  
  1987.     if(status = gf_pipe(gc, detach_so ? detach_writec : pc)) {
  1988.     sprintf(err_string, "Formatting error: %s", status);
  1989.         rv = 0L;
  1990.     }
  1991.  
  1992.     /*
  1993.      * If we wrote to a temporary area, there MAY be a user-defined
  1994.      * filter to invoke.  Filter it it (or not if no trigger match)
  1995.      * *AND* send the output thru any auxiliary filters, destroy the
  1996.      * temporary object and be done with it...
  1997.      */
  1998.     if(detach_so){
  1999.     if(!err_string[0] && display_filter && *display_filter){
  2000.         filter_t *p, *aux = NULL;
  2001.  
  2002.         if(aux_filters && *aux_filters){
  2003.         /* insert NL conversion filters around remaining aux_filters
  2004.          * so they're not tripped up by local NL convention
  2005.          */
  2006.         for(p = aux_filters; *p; p++)    /* count aux_filters */
  2007.           ;
  2008.  
  2009.         p = aux = (filter_t *)fs_get(((p - aux_filters) + 3)
  2010.                           * sizeof(filter_t));
  2011.         *p++ = gf_local_nvtnl;
  2012.         for(; *aux_filters; p++, aux_filters++)
  2013.           *p = *aux_filters;
  2014.  
  2015.         *p++ = gf_nvtnl_local;
  2016.         *p   = NULL;
  2017.         }
  2018.  
  2019.         if(status = dfilter(display_filter, detach_so, pc, aux)){
  2020.         sprintf(err_string, "Formatting error: %s", status);
  2021.         rv = 0L;
  2022.         }
  2023.  
  2024.         if(aux)
  2025.           fs_give((void **)&aux);
  2026.     }
  2027.     else{                    /*  just copy it, then */
  2028.         gf_set_so_readc(&gc, detach_so);
  2029.         so_seek(detach_so, 0L, 0);
  2030.         gf_filter_init();
  2031.         if(aux_filters && *aux_filters){
  2032.         /* if other filters are involved, correct for
  2033.          * newlines on either side of the pipe...
  2034.          */
  2035.         gf_link_filter(gf_local_nvtnl);
  2036.         while(*aux_filters)
  2037.           gf_link_filter(*aux_filters++);
  2038.  
  2039.         gf_link_filter(gf_nvtnl_local);
  2040.         }
  2041.  
  2042.         if(status = gf_pipe(gc, pc)){    /* Second pass, sheesh */
  2043.         sprintf(err_string, "Formatting error: %s", status);
  2044.         rv = 0L;
  2045.         }
  2046.     }
  2047.  
  2048.     so_give(&detach_so);            /* blast temp copy */
  2049.     }
  2050.  
  2051.   fini :
  2052.  
  2053. #if    defined(DOS) && !defined(WIN32)
  2054.     /*
  2055.      * free up file pointer, and delete tmpfile opened for
  2056.      * dos_gets.  Again, we can't use DOS' tmpfile() as it writes root
  2057.      * sheesh.
  2058.      */
  2059.     if(src == FileStar){
  2060.     fclose(append_file);
  2061.     append_file = NULL;
  2062.     unlink(tmpfile_name);
  2063.     fs_give((void **)&tmpfile_name);
  2064.     mail_parameters(stream, SET_GETS, (void *)NULL);
  2065.     mail_gc(stream, GC_TEXTS);
  2066.     }
  2067. #endif
  2068.  
  2069.     if(!ps_global->print && we_cancel)
  2070.       cancel_busy_alarm(0);
  2071.  
  2072.     if (len)
  2073.       *len = rv;
  2074.  
  2075.     if(df_trigger_list)
  2076.       blast_trigger_list(&df_trigger_list);
  2077.  
  2078.     return((err_string[0] == '\0') ? NULL : err_string);
  2079. }
  2080.  
  2081.  
  2082. /*
  2083.  * df_static_trigger - look thru the display filter list and set the
  2084.  *               filter to any triggers that don't require scanning
  2085.  *               the message segment.
  2086.  */
  2087. int
  2088. df_static_trigger(body)
  2089.     BODY *body;
  2090. {
  2091.     char **l;
  2092.     int    success = 0;
  2093.     char  *test = NULL, *cmd = NULL;
  2094.  
  2095.     for(l = ps_global->VAR_DISPLAY_FILTERS ; l && *l && !success; l++){
  2096.     get_pair(*l, &test, &cmd, 1);
  2097.  
  2098.     if(test){
  2099.         if(!*test){                /* null test means always! */
  2100.         success = 1;
  2101.         }
  2102.         else if(struncmp(test, "_CHARSET(", 9) == 0){
  2103.         PARAMETER *params = body->parameter;
  2104.         char *p = strrindex(test, ')');
  2105.         if(p){
  2106.             *p = '\0';
  2107.             while(params && strucmp(params->attribute, "charset"))
  2108.               params = params->next;
  2109.  
  2110.             success = !strucmp(test + 9,
  2111.                        params ? params->value : "us-ascii");
  2112.         }
  2113.         else
  2114.           dprint(1, (debugfile,
  2115.                  "filter trigger: malformed test: %s\n", test));
  2116.         }
  2117.  
  2118.         fs_give((void **)&test);        /* clean up test */
  2119.     }
  2120.     /* else empty list value */
  2121.  
  2122.     /*
  2123.      * make sure cmd exists!  NOTE: success overloaded
  2124.      */
  2125.     if(!(success && df_valid_command(&cmd))){
  2126.         success = 0;
  2127.         if(cmd)
  2128.           fs_give((void **)&cmd);
  2129.         else
  2130.           dprint(5, (debugfile, "filter trigger: EMPTY command!\n"));
  2131.     }
  2132.     }
  2133.  
  2134.     return((display_filter = cmd) != NULL);
  2135. }
  2136.  
  2137.  
  2138. /*
  2139.  * build_trigger_list - return possible triggers in a list of triggers 
  2140.  *            structs
  2141.  */
  2142. struct triggers *
  2143. build_trigger_list()
  2144. {
  2145.     struct triggers  *tp = NULL, **trailp;
  2146.     char        **l, *test, *str, *ep, *cmd = NULL;
  2147.     int              i;
  2148.  
  2149.     trailp = &tp;
  2150.     for(l = ps_global->VAR_DISPLAY_FILTERS ; l && *l; l++){
  2151.     get_pair(*l, &test, &cmd, 1);
  2152.     if(test && df_valid_command(&cmd)){
  2153.         *trailp      = (struct triggers *)fs_get(sizeof(struct triggers));
  2154.         (*trailp)->cmp = df_trigger_cmp_text;
  2155.         str           = test;
  2156.         if(*test == '_' && (i = strlen(test)) > 10
  2157.            && *(ep = &test[i-1]) == '_' && *--ep == ')'){
  2158.         if(struncmp(test, "_CHARSET(", 9) == 0){
  2159.             fs_give((void **)&test);
  2160.             fs_give((void **)&cmd);
  2161.             fs_give((void **)trailp);
  2162.             continue;
  2163.         }
  2164.  
  2165.         if(strncmp(test+1, "LEADING(", 8) == 0){
  2166.             (*trailp)->cmp = df_trigger_cmp_lwsp;
  2167.             *ep           = '\0';
  2168.             str           = cpystr(test+9);
  2169.             fs_give((void **)&test);
  2170.         }
  2171.         else if(strncmp(test+1, "BEGINNING(", 10) == 0){
  2172.             (*trailp)->cmp = df_trigger_cmp_start;
  2173.             *ep           = '\0';
  2174.             str           = cpystr(test+11);
  2175.             fs_give((void **)&test);
  2176.         }
  2177.         }
  2178.  
  2179.         (*trailp)->text = str;
  2180.         (*trailp)->cmd = cmd;
  2181.         *(trailp = &(*trailp)->next) = NULL;
  2182.     }
  2183.     else{
  2184.         fs_give((void **)&test);
  2185.         fs_give((void **)&cmd);
  2186.     }
  2187.     }
  2188.  
  2189.     return(tp);
  2190. }
  2191.  
  2192.  
  2193. /*
  2194.  * blast_trigger_list - zot any list of triggers we've been using 
  2195.  */
  2196. void
  2197. blast_trigger_list(tlist)
  2198.     struct triggers **tlist;
  2199. {
  2200.     if((*tlist)->next)
  2201.       blast_trigger_list(&(*tlist)->next);
  2202.  
  2203.     fs_give((void **)&(*tlist)->text);
  2204.     fs_give((void **)&(*tlist)->cmd);
  2205.     fs_give((void **)tlist);
  2206. }
  2207.  
  2208.  
  2209. /*
  2210.  * df_trigger_cmp - compare the line passed us with the list of defined
  2211.  *            display filter triggers
  2212.  */
  2213. int
  2214. df_trigger_cmp(n, s)
  2215.     long  n;
  2216.     char *s;
  2217. {
  2218.     register struct triggers *tp;
  2219.     int                  result;
  2220.  
  2221.     if(!display_filter)                /* already found? */
  2222.       for(tp = df_trigger_list; tp; tp = tp->next)
  2223.     if(tp->cmp){
  2224.         if((result = (*tp->cmp)(s, tp->text)) < 0)
  2225.           tp->cmp = NULL;
  2226.         else if(result > 0)
  2227.           return((display_filter = tp->cmd) != NULL);
  2228.     }
  2229.  
  2230.     return(0);
  2231. }
  2232.  
  2233.  
  2234. /*
  2235.  * df_trigger_cmp_text - return 1 if s1 is in s2
  2236.  */
  2237. int
  2238. df_trigger_cmp_text(s1, s2)
  2239.     char *s1, *s2;
  2240. {
  2241.     return(strstr(s1, s2) != NULL);
  2242. }
  2243.  
  2244.  
  2245. /*
  2246.  * df_trigger_cmp_lwsp - compare the line passed us with the list of defined
  2247.  *                 display filter triggers. returns:
  2248.  *
  2249.  *            0  if we don't know yet
  2250.  *            1  if we match
  2251.  *           -1  if we clearly don't match
  2252.  */
  2253. int
  2254. df_trigger_cmp_lwsp(s1, s2)
  2255.     char *s1, *s2;
  2256. {
  2257.     while(*s1 && isspace((unsigned char)*s1))
  2258.       s1++;
  2259.  
  2260.     return((*s1) ? (!strncmp(s1, s2, strlen(s2)) ? 1 : -1) : 0);
  2261. }
  2262.  
  2263.  
  2264. /*
  2265.  * df_trigger_cmp_start - return 1 if first strlen(s2) chars start s1
  2266.  */
  2267. int
  2268. df_trigger_cmp_start(s1, s2)
  2269.     char *s1, *s2;
  2270. {
  2271.     return(!strncmp(s1, s2, strlen(s2)));
  2272. }
  2273.  
  2274.  
  2275. /*
  2276.  * df_valid_command - make sure argv[0] of command really exists.
  2277.  *              "cmd" is required to be an alloc'd string since
  2278.  *              it will get realloc'd if the command's path is
  2279.  *              expanded.
  2280.  */
  2281. int
  2282. df_valid_command(cmd)
  2283.     char **cmd;
  2284. {
  2285.     int  i;
  2286.     char cpath[MAXPATH+1], *p;
  2287.  
  2288.     /*
  2289.      * copy cmd to build expanded path if necessary.
  2290.      */
  2291.     for(i = 0; cpath[i] = (*cmd)[i]; i++)
  2292.       if(isspace((unsigned char)(*cmd)[i])){
  2293.       cpath[i] = '\0';        /* tie off command's path*/
  2294.       break;
  2295.       }
  2296.  
  2297. #if    defined(DOS) || defined(OS2)
  2298.     if(is_absolute_path(cpath)){
  2299.     fixpath(cpath, MAXPATH);
  2300.     p = (char *) fs_get(strlen(cpath) + strlen(&(*cmd)[i]) + 1);
  2301.     strcpy(p, cpath);        /* copy new path */
  2302.     strcat(p, &(*cmd)[i]);        /* and old args */
  2303.     fs_give((void **) cmd);        /* free it */
  2304.     *cmd = p;            /* and assign new buf */
  2305.     }
  2306. #else
  2307.     if(cpath[0] == '~'){
  2308.     if(fnexpand(cpath, MAXPATH)){
  2309.         p = (char *) fs_get(strlen(cpath) + strlen(&(*cmd)[i]) + 1);
  2310.         strcpy(p, cpath);        /* copy new path */
  2311.         strcat(p, &(*cmd)[i]);    /* and old args */
  2312.         fs_give((void **) cmd);    /* free it */
  2313.         *cmd = p;            /* and assign new buf */
  2314.     }
  2315.     else
  2316.       return(FALSE);
  2317.     }
  2318. #endif
  2319.  
  2320.     return(is_absolute_path(cpath) && can_access(cpath, EXECUTE_ACCESS) == 0);
  2321. }
  2322.  
  2323.  
  2324.  
  2325. /*
  2326.  * dfilter - pipe the data from the given storage object thru the
  2327.  *         global display filter and into whatever the putchar's
  2328.  *         function points to.
  2329.  */
  2330. char *
  2331. dfilter(rawcmd, input_so, output_pc, aux_filters)
  2332.     char    *rawcmd;
  2333.     STORE_S *input_so;
  2334.     gf_io_t  output_pc;
  2335.     filter_t   *aux_filters;
  2336. {
  2337.     char *status = NULL, *cmd, *resultf = NULL, *tmpfile = NULL;
  2338.     int   key = 0;
  2339.  
  2340.     if(cmd = expand_filter_tokens(rawcmd, NULL, &tmpfile, &resultf, &key)){
  2341.     suspend_busy_alarm();
  2342. #ifndef    _WINDOWS
  2343.     ps_global->mangled_screen = 1;
  2344.     ClearScreen();
  2345.     fflush(stdout);
  2346. #endif
  2347.  
  2348.     /*
  2349.      * If it was requested that the interaction take place via
  2350.      * a tmpfile, fill it with text from our input_so, and let
  2351.      * system_pipe handle the rest...
  2352.      */
  2353.     if(tmpfile){
  2354.         PIPE_S      *filter_pipe;
  2355.         FILE      *fp;
  2356.         gf_io_t       gc, pc;
  2357.  
  2358.         /* write the tmp file */
  2359.         so_seek(input_so, 0L, 0);
  2360.         if(fp = fopen(tmpfile, WRITE_MODE)){
  2361.         /* copy input to tmp file */
  2362.         gf_set_so_readc(&gc, input_so);
  2363.         gf_set_writec(&pc, fp, 0L, FileStar);
  2364.         gf_filter_init();
  2365.         if(!(status = gf_pipe(gc, pc))){
  2366.             fclose(fp);        /* close descriptor */
  2367.             if(filter_pipe = open_system_pipe(cmd, NULL, NULL,
  2368.                               PIPE_USER | PIPE_RESET)){
  2369.             (void) close_system_pipe(&filter_pipe);
  2370.  
  2371.             /* pull result out of tmp file */
  2372.             if(fp = fopen(tmpfile, READ_MODE)){
  2373.                 gf_set_readc(&gc, fp, 0L, FileStar);
  2374.                 gf_filter_init();
  2375.                 while(aux_filters && *aux_filters)
  2376.                   gf_link_filter(*aux_filters++);
  2377.  
  2378.                 status = gf_pipe(gc, output_pc);
  2379.                 fclose(fp);
  2380.             }
  2381.             else
  2382.               status = "Can't read result of display filter";
  2383.             }
  2384.             else
  2385.               status = "Can't open pipe for display filter";
  2386.         }
  2387.  
  2388.         unlink(tmpfile);
  2389.         }
  2390.         else
  2391.           status = "Can't open display filter tmp file";
  2392.     }
  2393.     else if(status = gf_filter(cmd, key ? filter_session_key() : NULL,
  2394.                    input_so, output_pc, aux_filters)){
  2395.         int ch;
  2396.  
  2397.         fprintf(stdout,"\r\n%s  Hit return to continue.", status);
  2398.         fflush(stdout);
  2399.         while((ch = read_char(300)) != ctrl('M') && ch != NO_OP_IDLE)
  2400.           putchar(BELL);
  2401.     }
  2402.  
  2403.     if(resultf){
  2404.         if(name_file_size(resultf) > 0L)
  2405.           display_output_file(resultf, "Filter", NULL);
  2406.  
  2407.         fs_give((void **)&resultf);
  2408.     }
  2409.  
  2410.     resume_busy_alarm();
  2411. #ifndef    _WINDOWS
  2412.     ClearScreen();
  2413. #endif
  2414.     fs_give((void **)&cmd);
  2415.     }
  2416.  
  2417.     return(status);
  2418. }
  2419.  
  2420.  
  2421. /*
  2422.  * expand_filter_tokens - return an alloc'd string with any special tokens
  2423.  *              in the given filter expanded, NULL otherwise.
  2424.  */
  2425. char *
  2426. expand_filter_tokens(filter, env, tmpf, resultf, key)
  2427.     char      *filter;
  2428.     ENVELOPE  *env;
  2429.     char     **tmpf, **resultf;
  2430.     int       *key;
  2431. {
  2432.     char *cmd = NULL, *rlp = NULL, *tfp = NULL, *rfp = NULL,
  2433.      *dfp = NULL, *skp = NULL;
  2434.  
  2435.     /*
  2436.      * Here's where we scan the filter replacing:
  2437.      * _TMPFILE_        temp file name holding data to filter
  2438.      * _RECIPIENTS_        space delimited list of recipients
  2439.      * _DATAFILE_        file for filter invocations to keep state
  2440.      */
  2441.     rlp = strstr(filter, "_RECIPIENTS_");
  2442.     tfp = strstr(filter, "_TMPFILE_");
  2443.     rfp = strstr(filter, "_RESULTFILE_");
  2444.     dfp = strstr(filter, "_DATAFILE_");
  2445.     skp = strstr(filter, "_PREPENDKEY_");
  2446.     if(rlp || tfp || dfp || rfp || skp){
  2447.     char *tfn = NULL, *dfn = NULL, *rfn = NULL,  *rl = NULL;
  2448.  
  2449.     if(tfp){
  2450.         tfn = temp_nam(NULL, "sf");        /* send filter file */
  2451.         if(tmpf)
  2452.           *tmpf = tfn;
  2453.     }
  2454.  
  2455.     if(dfp)
  2456.       dfn = filter_data_file(1);        /* filter data file */
  2457.  
  2458.     if(rfp){
  2459.         rfn = temp_nam(NULL, "rf");        /* result to show the user */
  2460.         if(resultf)
  2461.           *resultf = rfn;
  2462.     }
  2463.  
  2464.     if(rlp && env){
  2465.         char *to_l = addr_list_string(env->to,
  2466.                       simple_addr_string, 0),
  2467.          *cc_l = addr_list_string(env->cc,
  2468.                       simple_addr_string, 0),
  2469.          *bcc_l = addr_list_string(env->bcc,
  2470.                        simple_addr_string, 0);
  2471.  
  2472.         rl = fs_get(strlen(to_l) + strlen(cc_l) + strlen(bcc_l) + 3);
  2473.         sprintf(rl, "%s %s %s", to_l, cc_l, bcc_l);
  2474.         fs_give((void **)&to_l);
  2475.         fs_give((void **)&cc_l);
  2476.         fs_give((void **)&bcc_l);
  2477.         for(to_l = rl; *to_l; to_l++)    /* to_l overloaded! */
  2478.           if(*to_l == ',')            /* space delim'd list */
  2479.         *to_l = ' ';
  2480.     }
  2481.  
  2482.     cmd = (char *)fs_get(strlen(filter) + 1
  2483.                  + ((tfn) ? strlen(tfn) : 0)
  2484.                  + ((rl) ? strlen(rl) : 0)
  2485.                  + ((rfn) ? strlen(rfn) : 0)
  2486.                  + ((dfn) ? strlen(dfn) : 0));
  2487.     strcpy(cmd, filter);
  2488.     if(rlp){
  2489.         rplstr(cmd + (rlp - filter), 12, rl ? rl : "");
  2490.         if(rl)
  2491.           fs_give((void **)&rl);
  2492.     }
  2493.  
  2494.     if(skp = strstr(cmd, "_PREPENDKEY_")){
  2495.         rplstr(skp, 12, "");
  2496.         if(key)
  2497.           *key = 1;
  2498.     }
  2499.  
  2500.     if(rfp = strstr(cmd, "_RESULTFILE_")){
  2501.         if(!rfn){
  2502.         rplstr(rfp, 12, "");        /* couldn't create it! */
  2503.         dprint(1, (debugfile, "FAILED creat of _RESULTFILE_\n"));
  2504.         }
  2505.         else
  2506.           rplstr(rfp, 12, rfn);
  2507.     }
  2508.  
  2509.     if(dfp = strstr(cmd, "_DATAFILE_")){
  2510.         if(!dfn){
  2511.         rplstr(dfp, 10, "");        /* couldn't create it! */
  2512.         dprint(1, (debugfile, "FAILED creat of _DATAFILE_\n"));
  2513.         }
  2514.         else
  2515.           rplstr(dfp, 10, dfn);
  2516.     }
  2517.  
  2518.     if(tfp = strstr(cmd, "_TMPFILE_"))
  2519.       rplstr(tfp, 9, tfn);
  2520.     }
  2521.     else
  2522.       cmd = cpystr(filter);
  2523.  
  2524.     return(cmd);
  2525. }
  2526.  
  2527.  
  2528.  
  2529.  
  2530. /*
  2531.  * filter_data_file - function to return filename of scratch file for
  2532.  *              display and sending filters.  This file is created
  2533.  *              the first time it's needed, and persists until pine
  2534.  *              exits.
  2535.  */
  2536. char *
  2537. filter_data_file(create_it)
  2538.     int create_it;
  2539. {
  2540.     static char *fn = NULL;
  2541.  
  2542.     if(!fn && create_it){
  2543.     if(fn = temp_nam(NULL, "df"))
  2544.       if(creat(fn, S_IREAD|S_IWRITE) < 0)    /* owner read/write */
  2545.         fs_give((void **)&fn);
  2546.     }
  2547.     
  2548.     return(fn);
  2549. }
  2550.  
  2551.  
  2552. /*
  2553.  * filter_session_key - function to return randomly generated number
  2554.  *            representing a key for this session.  The idea is
  2555.  *            the display/sending filter could use it to muddle
  2556.  *            up any pass phrase or such stored in the
  2557.  *            "_DATAFILE_".
  2558.  */
  2559. char *
  2560. filter_session_key()
  2561. {
  2562.     static char *key = NULL;
  2563.  
  2564.     if(!key){
  2565.     sprintf(tmp_20k_buf, "%ld", random());
  2566.     key = cpystr(tmp_20k_buf);
  2567.     }
  2568.     
  2569.     return(key);
  2570. }
  2571.